import json
import re
import time
from threading import Thread
import psutil
import requests
from socket import gethostbyname
from data import userData
from process import exeControl
from mcstatus import MinecraftBedrockServer


class robot:
    # 初始化信息
    def __init__(self, iniData: userData, dls: exeControl):
        self.online = True
        self.ini = iniData.iniCls
        self.data = iniData.dataDict
        self.dls = dls
        self.dlsLog = dls.eventLog
        self.host = f"http://{self.ini.robot_host}:{self.ini.robot_port}/"
        self.bot_qq = self.ini.robot_qq
        self.verifyKey = self.ini.robot_key
        self.owner = self.ini.robot_owner
        self.group = self.ini.robot_group
        self.group_cmd = self.ini.robot_group_cmd
        self.thread_list = []
        self.session_key = ""
        self.verify = ""

    # 获取session
    def getSession(self):
        try:
            session = requests.post(self.host + "verify", json={"verifyKey": self.verifyKey}).json()
            if session["code"]:
                return "验证失败"
            self.session_key = session["session"]
            self.verify = \
                requests.post(self.host + "bind", json={"sessionKey": self.session_key, "qq": self.bot_qq}).json()[
                    "code"]
            if self.verify:
                return "验证失败"
            return "连接成功"
        except:
            return "连接失败"

    # 私聊发送消息
    def sendFriendMessage(self, target, message):
        json = {"sessionKey": self.session_key,
                "target": target,
                "messageChain": [
                    {"type": "Plain", "text": message},
                ]}
        return requests.post(self.host + "sendFriendMessage", json=json).json()

    # 发送消息至群聊
    def sendGroupMessage(self, target, message):
        json = {"sessionKey": self.session_key,
                "target": target,
                "messageChain": [
                    {"type": "Plain", "text": message},
                ]}
        return requests.post(self.host + "sendGroupMessage", json=json).json()

    # 释放session
    def releaseSession(self):
        json = {"sessionKey": self.session_key, "qq": self.bot_qq}
        try:
            release = requests.post(self.host + "release", json=json).json()["code"]
            if release == 0:
                print("[DLS] 机器人正常断开")
            else:
                print(f"[DLS] 机器人异常断开：{release}")
        except:
            print(f"[DLS] 机器人异常断开")

    # 处理服务器日志转发至群中
    def dealDlsLog(self, wait_time):
        for log in self.dlsLog:
            time.sleep(wait_time)
            if log[0] == "sendGroup":
                self.sendGroupMessage(self.group, log[1])
            self.dlsLog.remove(log)

    # 事件处理
    def dealEvent(self):
        while self.online:
            time.sleep(0.6)
            # 判断连接是否正常
            self.dealConnect()
            # 处理服务器消息转群
            self.dealDlsLog(0)
            # 处理群消息命令
            fetch_cmd = self.host + f"fetchLatestMessage?sessionKey={self.session_key}&count=1"
            fetch_msg = requests.get(fetch_cmd).json()["data"]
            if fetch_msg:  # 消息为空时不处理
                fetch_msg = fetch_msg[0]
                try:
                    # 群聊消息
                    if fetch_msg["type"] == "GroupMessage":
                        if fetch_msg["sender"]["group"]["id"] == self.group:
                            if len(fetch_msg["messageChain"]) > 1:
                                if fetch_msg["messageChain"][1]["type"] == "Plain":
                                    text: str = fetch_msg["messageChain"][1]["text"]
                                    # 分权限执行命令
                                    for gCmd in self.group_cmd:
                                        if re.match(gCmd["识别头"], text):
                                            # 转换命令
                                            p_msg = re.sub(gCmd["识别头"], "", text, count=1).strip()
                                            n_msg = fetch_msg["sender"]["memberName"]
                                            qq_msg = fetch_msg["sender"]["id"]
                                            cmd = gCmd["执行命令"].replace("%p", p_msg).replace("%n", n_msg)
                                            # 分权限处理
                                            if gCmd.get("全员可用") or fetch_msg["sender"]["id"] in self.owner:
                                                self.countAndDocmd(gCmd, p_msg, qq_msg, cmd)
                    # 私聊消息
                    elif fetch_msg["type"] == "FriendMessage":
                        if fetch_msg["sender"]["id"] in self.owner:
                            if len(fetch_msg["messageChain"]) > 1:
                                if fetch_msg["messageChain"][1]["type"] == "Plain":
                                    text: str = fetch_msg["messageChain"][1]["text"]
                                    if text == "服务器状态":
                                        self.sendFriendMessage(fetch_msg["sender"]["id"], self.getStatus())
                                    else:
                                        self.doCmd(text)
                except Exception as err:
                    error_msg = f"\n[DLS][ERROR] 异常的消息！ 错误: {err}  消息json: {fetch_msg}\n"
                    print(error_msg)

    # 命令记录与执行
    def countAndDocmd(self, gCmd, p_msg, qq_msg, cmd):
        qq_str = str(qq_msg)
        if gCmd.get("记录"):
            # 字典初始化
            if not self.data.get("cmdTimes"):
                self.data["cmdTimes"] = {}
            if not self.data["cmdTimes"].get(gCmd["记录"]):
                self.data["cmdTimes"][gCmd["记录"]] = {}
            # 用户初始化
            if not self.data["cmdTimes"][gCmd["记录"]].get(qq_str):
                self.data["cmdTimes"][gCmd["记录"]][qq_str] = \
                    {"次数": 0, "记录值": []}
            # 判断与计数
            if self.data["cmdTimes"][gCmd["记录"]][qq_str]["次数"] < gCmd.get("限制次数", 0):
                if self.doCmd(cmd):
                    self.data["cmdTimes"][gCmd["记录"]][qq_str]["次数"] += 1
                    self.data["cmdTimes"][gCmd["记录"]][qq_str]["记录值"].append(p_msg)
                    self.saveDataToFile("cmdTimes", self.data["cmdTimes"], "DLS/数据/命令次数记录.json")
                    self.sendGroupMessage(self.group, f'ID: {qq_str}\n记录值: '
                                                      f'{", ".join(self.data["cmdTimes"][gCmd["记录"]][qq_str]["记录值"])}')
                else:
                    self.sendGroupMessage(self.group, "命令执行失败, 记录被撤销！")
            elif gCmd.get("限制次数", 0) == -1 or qq_msg in self.owner:
                self.doCmd(cmd)
                self.data["cmdTimes"][gCmd["记录"]][qq_str]["次数"] += 1
                self.data["cmdTimes"][gCmd["记录"]][qq_str]["记录值"].append(p_msg)
                self.saveDataToFile("cmdTimes", self.data["cmdTimes"], "DLS/数据/命令次数记录.json")
                self.sendGroupMessage(self.group, f'ID: {qq_str}\n记录值: '
                                                  f'{", ".join(self.data["cmdTimes"][gCmd["记录"]][qq_str]["记录值"])}')
            else:
                self.sendGroupMessage(self.group, f'命令次数已用完！\nID: {qq_str}\n记录值: '
                                                  f'{", ".join(self.data["cmdTimes"][gCmd["记录"]][qq_str]["记录值"])}')
        else:
            self.doCmd(cmd)

    # 移除指定记录
    def timesReset(self, record, qq):
        if self.data.get("cmdTimes"):
            if self.data["cmdTimes"].get(record):
                if self.data["cmdTimes"][record].get(qq):
                    self.data["cmdTimes"][record][qq] = {}
                    self.saveDataToFile("cmdTimes", self.data["cmdTimes"], "DLS/数据/命令次数记录.json")
                    return "移除记录成功"

    # 查询指定记录
    def timesSearch(self, record, qq):
        if self.data.get("cmdTimes"):
            if self.data["cmdTimes"].get(record):
                if self.data["cmdTimes"][record].get(qq):
                    return f'ID: {qq}\n记录值: {", ".join(self.data["cmdTimes"][record][qq]["记录值"])}'
        return f"未找到相关记录"

    # 文件的保存
    def saveDataToFile(self, dataKey, dataContent, file_path):
        file_path = self.dls.control_path + "/" + file_path
        dataDict = {dataKey: dataContent}
        try:
            with open(file_path, "w", encoding="utf-8") as f:
                f.write(json.dumps(dataDict, sort_keys=True, indent=4, ensure_ascii=False))
        except Exception as err:
            print(f"[DLS][ERROR] 文件{file_path}保存失败！错误原因: {err}！")

    # 执行命令
    def doCmd(self, command):
        if command == "start":
            self.dls.exeStart()
        elif command == "stop":
            self.dls.exeStop()
        elif command == "status":
            self.sendGroupMessage(self.group, self.getStatus())
        elif re.match("group", command):
            group_text = re.sub("group", "", command, count=1).strip()
            self.sendGroupMessage(self.group, group_text)
        elif re.match("cmd", command):
            command = re.sub("cmd", "", command, count=1).strip()
            return self.dls.executeCmd(command)
        elif re.match("chat", command):
            msg_text = re.sub("chat", "", command, count=1).strip()
            raw_json = {"rawtext": [{"text": msg_text}]}
            command = f"tellraw @a {raw_json}".replace("\'", "\"")
            return self.dls.executeCmd(command)
        elif re.match("motdpe", command):
            command = re.sub("motdpe", "", command, count=1).strip()
            self.sendGroupMessage(self.group, self.motdPe(command))
        elif re.match("blackbe", command):
            command = re.sub("blackbe", "", command, count=1).strip()
            self.sendGroupMessage(self.group, self.blackBe(command))
        elif re.match("xuid", command):
            command = re.sub("xuid", "", command, count=1).strip()
            self.sendGroupMessage(self.group, self.xuidSearch(command))
        elif re.match("timesReset", command):
            command = re.sub("timesReset", "", command, count=1).strip()
            record = command.split(" ")[0]
            qq = command.split(" ")[1]
            self.sendGroupMessage(self.group, self.timesReset(record, qq))
        elif re.match("timesSearch", command):
            command = re.sub("timesSearch", "", command, count=1).strip()
            record = command.split(" ")[0]
            qq = command.split(" ")[1]
            self.sendGroupMessage(self.group, self.timesSearch(record, qq))
        return True

    # 判断连接是否正常
    def dealConnect(self):
        try:
            connect_status = requests.get(self.host + "about").json()["code"]
            if connect_status:
                self.releaseSession()
                print("[DLS][WARN] 机器人连接异常！静默重连已启用~")
                self.reConnect()
        except:
            print("[DLS][WARN] 机器人连接已断开！静默重连已启用~")
            self.reConnect()

    # 重连的处理
    def reConnect(self):
        while True:
            time.sleep(3)
            if self.getSession() == "连接成功":
                print("[DLS] 机器人已成功连接！")
                # 直到能发送群消息再继续
                while self.sendGroupMessage(self.group, "机器人重新连接！")["code"]:
                    time.sleep(1)
                self.dealDlsLog(2)
                break

    # 启动线程
    def start(self):
        # 未设定启用时退出
        if not self.ini.robot_on:
            return
        # 开始启动
        info = self.getSession()
        if info in ["验证失败", "连接失败"]:
            print(f"[DLS][ERROR] 机器人{info}！请检查配置无误后重启")
            self.ini.robot_on = False
            return
        th = Thread(target=self.dealEvent, daemon=True)
        th.start()
        self.thread_list.append(th)
        print(f"[DLS] 机器人成功连接！")

    # 关闭线程
    def stop(self):
        # 未设定启用时退出
        if not self.ini.robot_on:
            return
        # 关闭线程
        self.online = False
        for th in self.thread_list:
            th.join()
        self.thread_list = []
        self.releaseSession()

    # 重加载配置
    def iniReload(self, iniData):
        self.ini = iniData.iniCls
        self.data = iniData.dataDict
        self.host = f"http://{self.ini.robot_host}:{self.ini.robot_port}/"
        self.bot_qq = self.ini.robot_qq
        self.verifyKey = self.ini.robot_key
        self.owner = self.ini.robot_owner
        self.group = self.ini.robot_group
        self.group_cmd = self.ini.robot_group_cmd
        self.thread_list = []

    # 基岩版motd api
    @staticmethod
    def motdPe(host):
        address = host
        try:
            # 地址的处理
            if re.search(":[0-9]{1,5}", host):
                domain = host.split(":")[0]
                port = host.split(":")[1]
                host = f"{gethostbyname(domain)}:{port}"
            else:
                host = gethostbyname(host)
                host += ":19132"
            # 开始获取信息
            McStatus = MinecraftBedrockServer.lookup(host).status()
            msg = f"[MCBE服务器信息]\n状态: online\n地址: {address}\nmotd: {McStatus.motd}\n" \
                  f"协议版本: {McStatus.version.protocol}\n游戏版本: {McStatus.version.version}\n" \
                  f"在线人数: {McStatus.players_online}/{McStatus.players_max}\n存档名称: {McStatus.map}\n" \
                  f"游戏模式: {McStatus.gamemode}\n延迟: {round(McStatus.latency * 1000, 3)}ms"
            return msg
        except Exception as err:
            return f"[MCBE服务器信息]\n操作失败：{err}"

    # 云黑查询 api
    def blackBe(self, player):
        url = f"https://api.blackbe.xyz/openapi/v3/check/?name={player}"
        # 若为qq号则切换url
        re_player = re.match("[0-9]+", player)
        if re_player:
            if re_player.group() == player:
                url = f"https://api.blackbe.xyz/openapi/v3/check/?qq={player}"
        try:
            back = requests.get(url, timeout=5).json()
            if back["status"] != 2000:
                return back['message'] + "~"
            else:
                back['message'] = "存在违规行为"
            # 调用api根据玩家名查询xuid
            uid_player = self.xuidSearch(back['data']['info'][0]['name'])
            if uid_player == "查询失败~":
                uid_player = back['data']['info'][0]['xuid']
            # 生成云黑信息并返回
            msg = f"[MCBE云黑信息]\n" \
                  f"玩家id: {back['data']['info'][0]['name']}\n" \
                  f"记录qq: {back['data']['info'][0]['qq']}\n" \
                  f"玩家xuid: {uid_player}\n" \
                  f"记录状态: {back['message']}\n" \
                  f"记录级别: {back['data']['info'][0]['level']}\n" \
                  f"条目uuid: {back['data']['info'][0]['uuid']}\n" \
                  f"记录原因: {back['data']['info'][0]['info']}"
            return msg
        except Exception as err:
            print(f"[DLS][ERROR] 云黑查询失败！错误原因: {err}！")
            return "响应超时~"

    # xuid查询 api
    @staticmethod
    def xuidSearch(name):
        try:
            uid_data = requests.get(
                f"https://api.blackbe.xyz/openapi/v3/utils/xuid?gamertag={name}",
                timeout=10).json()
            return uid_data['data']['xuid']
        except Exception as err:
            print(f"[DLS][ERROR] XUID查询失败！错误原因: {err}！")
            return "查询失败~"

    # 获取服务器状态
    @staticmethod
    def getStatus():
        mem = psutil.virtual_memory()
        disks = psutil.disk_partitions()
        cpu = psutil.cpu_percent(1)
        info = f"CPU使用: {cpu}%\n内存使用:\n{int(mem.used / 1024 ** 2)}MB/{int(mem.total / 1024 ** 2)}MB\n磁盘使用:"
        for disk in disks:
            try:
                disk_info = psutil.disk_usage(disk.device)
                disk_head = disk.device.replace("\\", "")
            except:
                continue
            info += f"\n{disk_head} {int(disk_info.used / 1024 ** 3)}GB/{int(disk_info.total / 1024 ** 3)}GB"
        return info
